package xginplus

import (
	"encoding/json"
	"fmt"
	"io"
	"time"

	"github.com/gin-gonic/gin"
)

// 封装 gin.Context 结构体，用于提供增强功能
type Context struct {
	Jitter     bool          // 是否启用网络抖动过滤
	JitterTime time.Duration // 缓存时间，默认1秒（此值会影响网络抖动的缓存时间）
	TimeOut    time.Duration // 接口超时时间 0-不超时
	*gin.Context
}

// 分页处理，提取其中的分页参数
func (c *Context) page_load() {
	if c.Page() > 0 {
		return
	}
	// 后面的参数会覆盖掉之前的参数
	// 页码信息，允许的值有：p0、_page、page
	// 跳过条数，允许的值有：_offsite、offsite（此值会覆盖掉页码参数）
	// 每页查询条数，允许的值有：p1、_size、size、_limit、limit
	type temps struct {
		P0       json.Number `json:"p0" form:"p0" xml:"p0"`                   // 页码信息
		PPage    json.Number `json:"_page" form:"_page" xml:"_page"`          // 页码信息
		Page     json.Number `json:"page" form:"page" xml:"page"`             // 页码信息
		POffsite json.Number `json:"_offsite" form:"_offsite" xml:"_offsite"` // 跳过条数
		Offsite  json.Number `json:"offsite" form:"offsite" xml:"offsite"`    // 跳过条数
		P1       json.Number `json:"p1" form:"p1" xml:"p1"`                   // 每页查询条数
		PSize    json.Number `json:"_size" form:"_size" xml:"_size"`          // 每页查询条数
		Size     json.Number `json:"size" form:"size" xml:"size"`             // 每页查询条数
		PLimit   json.Number `json:"_limit" form:"_limit" xml:"_limit"`       // 每页查询条数
		Limit    json.Number `json:"limit" form:"limit" xml:"limit"`          // 每页查询条数
	}
	tmp := temps{}
	if err := c.Bind(&tmp); err != nil {
		return
	}
	page, offsite, limit := jint(tmp.P0, false), jint(tmp.POffsite, false), jint(tmp.P1, true)
	if page == 0 {
		page = jint(tmp.PPage, false)
		if page == 0 {
			page = jint(tmp.Page, false)
			if page == 0 {
				page = 1
			}
		}
	}
	if offsite == 0 {
		offsite = jint(tmp.Offsite, false)
	}
	if offsite > 0 {
		page = 1
	}
	if limit == 0 {
		limit = jint(tmp.PSize, true)
		if limit == 0 {
			limit = jint(tmp.Size, true)
			if limit == 0 {
				limit = jint(tmp.PLimit, true)
				if limit == 0 {
					limit = jint(tmp.Limit, true)
					if limit == 0 {
						limit = 20
					}
				}
			}
		}
	}
	if offsite == 0 {
		// 没有传入offset时
		offsite = (page - 1) * limit
	}
	c.Set("page", page)
	c.Set("offset", offsite)
	c.Set("limit", limit)
}

// 获取当前页码
func (c *Context) Page() int {
	return c.GetInt("page")
}

// 获取跳过条数
func (c *Context) Offset() int {
	return c.GetInt("offset")
}

// 获取每页查询条数
func (c *Context) Limit() int {
	return c.GetInt("limit")
}

// 获取原始POST请求体信息
func (c *Context) GetRequestBody() ([]byte, error) {
	var body []byte
	var err error
	// 因为gin框架会自动解析post内容并设置到c.Get中，所以此处先使用GET进行判断有没有缓存的body数据
	if cb, ok := c.Get(gin.BodyBytesKey); ok {
		if cbb, ok := cb.([]byte); ok {
			body = cbb
		}
	}
	if body == nil {
		body, err = io.ReadAll(c.Request.Body)
		if err != nil {
			return nil, err
		}
	}
	return body, nil
}

// 返回给前端的数据信息
type Response struct {
	code int // HTTP-Code代码
	data any // 返回的结构体信息
}

// 获取本次请求是否启用抖动过滤
func (c *Context) hasJitter() bool {
	if _default.Jitter {
		return true
	}
	return c.Jitter
}

// 返回成功数据
//
//	data	待返回的数据结构
func (c *Context) Success(data gin.H) *Response {
	code, resp := _default.SuccessData(data)
	return &Response{
		code: code,
		data: resp,
	}
}

// 返回成功数据
//
//	data	待返回的数据结构
func (c *Context) SuccessAny(data any) *Response {
	code, resp := _default.SuccessData(data)
	return &Response{
		code: code,
		data: resp,
	}
}

// 返回成功数据 空结构 等同于 c.Success(gin.H{})
func (c *Context) SuccessEmpty() *Response {
	return c.Success(gin.H{})
}

// 无分页数据返回
// 内部实现为采用分页形式来进行实现具体数据
//
//	count	查询到的条数
//	lis		列表数据
//	exter	附加数据列表
func (c *Context) SuccessNoPage(count int, lis any, exter ...gin.H) *Response {
	return c.SuccessPage(int64(count), lis, exter...)
}

// 分页返回
//
//	count	查询到的条数
//	lis		列表数据
//	exter	附加数据列表
func (c *Context) SuccessPage(count int64, lis any, exter ...gin.H) *Response {
	ext := gin.H{}
	for i := 0; i < len(exter); i++ {
		for k, v := range exter[i] {
			ext[k] = v
		}
	}
	code, resp := _default.SuccessPage(c, count, lis, ext)
	return &Response{
		code: code,
		data: resp,
	}
}

// 基础错误信息值返回
//
//	err	错误信息
func (c *Context) Error(err error) *Response {
	code, resp := _default.ErrorData(err)
	return &Response{
		code: code,
		data: resp,
	}
}

// 字符串基础错误信息值返回，等同于 c.Error(fmt.Errorf(xxx,xxx...))
//
//	format	格式化字符串的指定格式
//	a		待追入的参数
func (c *Context) Errorf(format string, a ...any) *Response {
	code, resp := _default.ErrorData(fmt.Errorf(format, a...))
	return &Response{
		code: code,
		data: resp,
	}
}

// 基础错误信息值返回
//
//	code	自定义错误码
//	err		错误字符串
func (c *Context) ErrorC(code int, err error) *Response {
	code, resp := _default.ErrorDataCode(code, err)
	return &Response{
		code: code,
		data: resp,
	}
}

// 字符串基础错误信息值返回，等同于 c.Error(fmt.Errorf(xxx,xxx...))
//
//	code	自定义错误码
//	format	格式化字符串的指定格式
//	a		待追入的参数
func (c *Context) ErrorCf(code int, format string, a ...any) *Response {
	code, resp := _default.ErrorDataCode(code, fmt.Errorf(format, a...))
	return &Response{
		code: code,
		data: resp,
	}
}

// 获取返回的数据信息
func (c *Response) GetData() any {
	return c.data
}

// 获取返回的Code代码和数据信息
func (c *Response) GetCodeData() (int, any) {
	return c.code, c.data
}

// 下载描述结构体
type DS struct {
	UTF8BOM     bool          // 是否写入UTF-8的BOM头文件
	Name        string        // 下载文件名
	Body        []byte        // 下载内容流
	CacheTime   time.Duration // 缓存时间
	ContentType string        // 文件类型，默认为application/octet-stream
}

// 下载文件【读取二进制流进行下载】
//
//	inf	待下载的文件结构信息
func (c *Context) Download(inf *DS) *Response {
	if inf == nil || len(inf.Body) == 0 {
		return c.Errorf("可下载内容为空")
	}
	if inf.ContentType != "" {
		c.Writer.Header().Set("Content-Type", inf.ContentType)
	} else {
		c.Writer.Header().Set("Content-Type", "application/octet-stream")
	}
	if inf.Name != "" {
		c.Writer.Header().Set("Content-Disposition", "attachment;filename="+inf.Name)
	} else {
		c.Writer.Header().Set("Content-Disposition", "attachment;filename="+time.Now().Format(time.DateTime)+".dat")
	}
	if inf.UTF8BOM && len(inf.Body) > 3 && (inf.Body[0] != 0xef || inf.Body[1] != 0xbb || inf.Body[2] != 0xbf) {
		// 写入UTF8的BOM头
		inf.Body = append([]byte("\xEF\xBB\xBF"), inf.Body...)
	}
	if inf.CacheTime > 0 {
		c.Header("Cache-Control", fmt.Sprintf("max-age=%d, public", int(inf.CacheTime.Seconds())))
	}
	c.Header("Accept-Ranges", "bytes")
	c.Header("Content-Length", fmt.Sprintf("%d", len(inf.Body)))
	c.Writer.Write(inf.Body)
	return nil
}
